#!/usr/bin/perl -w
# SPDX-License-Identifier: GPL-2.0

#
# Copyright  2008 Sun Microsystems, Inc. All rights reserved
# Use is subject to license terms.
#
# Copyright (c) 2013, 2014, Intel Corporation.
#

#
# This file is part of Lustre, http://www.lustre.org/
#
# Author: Jitendra Pawar <jitendra@clusterfs.com>
#

# Report generation for iokit-plot-obdfilter
# ====================================
# The iokit-plot-obdfilter script is used to generate csv file and
# instructions files for gnuplot from the output of obdfilter-survey script.
#
# The iokit-plot-obdfilter also creates .scr file that contains instructions
# for gnuplot to plot the graph. After generating .dat and .scr files this
# script invokes gnuplot to display graph.
#
# Syntax:
# $ obdfilter-survey > log_filename
# $ iokit-plot-obdfilter <log_filename>
# [Note: 1. Please use the .summary file generated by obdfilter-survey as log_file.
#           It is generally available at /tmp/obdfilter_survey_<date_time_system>.summary
#        2. This script may need modifications whenever there will be
#           modifications in output format of obdfilter-survey script.
#        3. Gnuplot version 4.0 or above is required.]

my @GraphTitle;
sub usage() {
	print "Usage: $0 <log_filename> [--st=<subtitle>] [--y0=<Y-axis start point>]\n";
	print "       The $0 parses and plots graphs for output of obdfilter-survey using gnuplot.\n";
	print "       It generates <log_filename>-<Rsize><rd/wr>.dat and\n";
	print "       <log_filename>-<Rsize>-<rd/wr/rrd/rwr/rwa>.scr files.\n";
	print "       Those will be used for graphing the results\n";
	print "OPTIONS:\n";
	print "	--st: SubTitle for the graph\n";
	print "	--y0: Start point of Y-axis, Default value automatically taken based on Y-axis values ranges\n";
	print "	log_file: use the .summary file generated by obdfilter-survey as log_file.\n";
	print "	          It is generally available at /tmp/obdfilter_survey_<date_time_system>.summary\n";
	print "e.g.   # $0 obdfilter-log --st=\"Sub-Title\" --y0=50\n";
	exit 1;
}

# check whether gnuplot exists?
system ("which gnuplot > /dev/null") == 0 or die "gnuplot does not exist, please install it and try again.\n";

#Subroutine to write .scr file that further used by gnuplot to plot the graph.
sub write_scr_file() {
	my $op = $_[0];
	my $rwlabel = $_[1];
	print "generating plot $file-$rsz-$op.png\n";
	open ( SCRFILE, "> $file-$rsz-$op.scr" ) or die "Can't open scr file for writing";

	if ($subtitle) {
		print SCRFILE "set title \"@GraphTitle\\n$rwlabel, Rsize = $rsz KBytes, $subtitle\"\n";
	} else {
		print SCRFILE "set title \"@GraphTitle\\n$rwlabel, Rsize = $rsz KBytes\"\n";
	}
	print SCRFILE "set xlabel \"Threads\"\n";
	print SCRFILE "set ylabel \"Speeds(MB/s)\"\n";
	print SCRFILE "set logscale x\n";
	print SCRFILE "set grid\n";
	print SCRFILE "set terminal png\n";
	print SCRFILE "set output \"/dev/null\"\n";
	if ($opt_y0 != -9999) {
		print SCRFILE "set yrange [ $opt_y0: ]\n";
	}
	my $plot = "plot";
	$i = 2;
	$xrange = 1;
	# generate instructions for gnuplot, with adjusting X-axes ranges
	for ($j = $first_obj; $j <= $obj ; $j = $j + $j) {
			printf SCRFILE "$plot \"$file-$rsz-$op.dat\" using 1:$i axes x%dy1 title \"$rwlabel-obj$j\" with line\n", $xrange;
		$i++;
		$plot = "replot";
	}
	print SCRFILE "set output \"$file-$rsz-$op.png\"\n";
	print SCRFILE "replot\n";
	close SCRFILE;
	$graphgen = 1;
	# invoke gnuplot to display graph.
	system ("gnuplot $file-$rsz-$op.scr") == 0 or die "ERROR: while ploting graph";
	system ("rm $file-$rsz-$op.scr");
}

#Subroutine to write .dat file that further used by gnuplot to plot the graph.
sub write_dat_file() {
	my $op = $_[0];
	print "writing data $file-$rsz-$op.dat\n";
	# Open .csv/.dat file for writing required columns from log file.
	open ( DATAFILE, "> $file-$rsz-$op.dat" ) or die "Can't open csv file for writing";
	printf DATAFILE "%-6s", "thrd";
	for ($j = $first_obj; $j <= $obj; $j = $j + $j) {
		printf DATAFILE "%-10s", "$op-obj$j";
	}
	for ( $i = $first_thread; $i <= $thread; $i = $i + $i ) {
	        printf DATAFILE "\n%-6s", $i;
		for ($j = $first_obj; $j <= $obj; $j = $j + $j) {
		# switch-case can be used instead if else
			if ($op eq "rd") {
					if ( $ard{$i}{$j} ) {
						printf DATAFILE "%-10s", $ard{$i}{$j};
					} else {
						printf DATAFILE "%-10s", "-";
					}
			} elsif ($op eq "wr" ) {
					if ( $awr{$i}{$j} ) {
						printf DATAFILE "%-10s", $awr{$i}{$j};
					} else {
						printf DATAFILE "%-10s", "-";
					}
			} elsif ($op eq "rwr" ) {
					if ( $arwr{$i}{$j} ) {
						printf DATAFILE "%-10s", $arwr{$i}{$j};
					} else {
						printf DATAFILE "%-10s", "-";
					}
			} elsif ($op eq "rrd" ) {
					if ( $arrd{$i}{$j} ) {
						printf DATAFILE "%-10s", $arrd{$i}{$j};
					} else {
						printf DATAFILE "%-10s", "-";
					}
			} elsif ($op eq "rwa" ) {
					if ( $arwa{$i}{$j} ) {
						printf DATAFILE "%-10s", $arwa{$i}{$j};
					} else {
						printf DATAFILE "%-10s", "-";
					}
			}
		}
	}
	close DATAFILE;
}

#Subroutine to call .scr and .dat file write routines.
sub write_files() {
	for ($cnt = 0; $cnt < @operations; $cnt = $cnt + 1) {
		# switch-case can be used instead if else
		if($operations[$cnt] eq "read") {
			&write_dat_file("rd");
			&write_scr_file("rd", "read");
		} elsif ($operations[$cnt] eq "write") {
			&write_dat_file("wr");
			&write_scr_file("wr", "write");
		} elsif ($operations[$cnt] eq "reread") {
			&write_dat_file("rrd");
			&write_scr_file("rrd", "reread");
		} elsif ($operations[$cnt] eq "rewrite") {
			&write_dat_file("rwr");
			&write_scr_file("rwr", "rewrite");
		} elsif ($operations[$cnt] eq "rewrite_again") {
			&write_dat_file("rwa");
			&write_scr_file("rwa", "rewrite_again");
		}
	}
}

if ( !$ARGV[0] ) {
	usage();
}
$file = $ARGV[0];
$obj = 0;
$thread = 0;
$first_obj = 1;
$first_thread = 1;
$count = 0;
$rsz = 0;
$subtitle = "";
$opt_y0 = -9999;
$cnt = 0;
@operations = ();
$graphgen = 0;
# Command line parameter parsing
use Getopt::Long;
GetOptions ('help' => \$opt_help, 'st=s' => \$subtitle, 'y0=i' => \$opt_y0) or usage();
if ($opt_help) {
	usage();
}
open ( PFILE, "$file") or die "Can't open results";
LABEL: while ( <PFILE> ) {
	chomp;
	@line = split( /\s+/ );
	if ($count == 0) {
		@GraphTitle = @line;
		$count++;
		next LABEL;
	}
	$linelen = @line;
	if ($linelen > 26 || $linelen < 11) {
		print "invalid file format at line $count\n";
		exit 1;
	}
	if (!$rsz && $line[5]) {
		$cnt = 0;
		$rsz = $line[5];
		$first_obj = $line[7];
		$first_thread = $line[9];
		for ($i = 10; $i <= $linelen; $i = $i + 5) {
			if ($line[$i]) {
				$operations[$cnt] = $line[$i];
				$cnt++;
			}
		}
	}
	if ($rsz ne $line[5]) {
		&write_files();
		$rsz = $line[5];
		$first_obj = $line[7];
		$first_thread = $line[9];
		@operations = ();
		$cnt = 0;
		for ($i = 10; $i <= $linelen; $i = $i + 5) {
			if ($line[$i]) {
				$operations[$cnt] = $line[$i];
				$cnt++;
			}
		}
		$obj = 0;
		$thread = 0;
	}
	for ($i = 0; $i < @operations; $i++) {
		# switch-case can be used instead if else
		if($operations[$i] eq "read") {
			$ard{$line[9]}{$line[7]} = $line[$i * 5 + 11];
		} elsif ($operations[$i] eq "write") {
			$awr{$line[9]}{$line[7]} = $line[$i * 5 + 11];
		} elsif ($operations[$i] eq "reread") {
			$arrd{$line[9]}{$line[7]} = $line[$i * 5 + 11];
		} elsif ($operations[$i] eq "rewrite") {
			$arwr{$line[9]}{$line[7]} = $line[$i * 5 + 11];
		} elsif ($operations[$i] eq "rewrite_again") {
			$arwa{$line[9]}{$line[7]} = $line[$i * 5 + 11];
		}
	}
	if ( $obj < $line[9] ) {
		$obj = $line[9];
	}
	if ( $thread < $line[7] ) {
		$thread = $line[7];
	}
	$count++;
}
close PFILE;
if ($count > 1 && $rsz) {
	&write_files();
}
if (!$graphgen) {
	print "Invalid log file format\n";
}
